#include <QtGui>
#include <cmath>

#include "paintchannel.h"

PaintChannel::PaintChannel(QWidget *parent) :
        QWidget(parent)
{
    setBackgroundRole(QPalette::Dark);
    setAutoFillBackground(true);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setFocusPolicy(Qt::StrongFocus);
    rubberBandIsShown = false;

    zoomInButton = new QToolButton(this);
    zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
    zoomInButton->adjustSize();
    connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));

    zoomOutButton = new QToolButton(this);
    zoomOutButton->setIcon(QIcon(":/images/zoomout.png"));
    zoomOutButton->adjustSize();
    connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
    setPlotSettings(PaintSettings());

}

void PaintChannel::setPlotSettings(const PaintSettings &settings)
{
        zoomStack.clear();
        zoomStack.append(settings);
        curZoom = 0;
        refreshPixmap();
}
int PaintChannel::getPhasic()
{
        return phasic;
}

void PaintChannel::setCurveData(const QVector<QPointF> &data)
{
        this->data = data;
        refreshPixmap();
}

QSize PaintChannel::minimumSizeHint() const
{
        return QSize(4 * Margin, 3 * Margin);
}

QSize PaintChannel::sizeHint() const
{
        return QSize(10 * Margin, 10 * Margin);
}

void PaintChannel::paintEvent(QPaintEvent * /* event */)
{
    QStylePainter painter(this);
    painter.drawPixmap(0, 0, pixmap);

    if (rubberBandIsShown)
    {
            painter.setPen(palette().light().color());
            painter.drawRect(rubberBandRect.normalized() .adjusted(0, 0, -1, -1));
    }

    if (hasFocus())
    {
            QStyleOptionFocusRect option;
            option.initFrom(this);
            option.backgroundColor = palette().dark().color();
            painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
    }
}

void PaintChannel::resizeEvent(QResizeEvent * /* event */)
{

    int x = width() - (zoomInButton->width() + zoomOutButton->width() + 10);
    zoomInButton->move(x, 5);
    zoomOutButton->move(x + zoomInButton->width() + 5, 5);
    refreshPixmap();
}
void PaintChannel::refreshPixmap()
{
        pixmap = QPixmap(size());
        pixmap.fill(this, 0, 0);
        QPainter painter(&pixmap);
        painter.initFrom(this);
        drawGrid(&painter);
        drawCurves(&painter);
        update();
}

void PaintChannel::drawGrid(QPainter *painter)
{
        QRect rect(Margin, 30, width() - 85, height() - 2 * 30);
        if (!rect.isValid())
                return;

        PaintSettings settings = zoomStack[curZoom];
        QPen quiteDark = palette().dark().color().light();
        QPen light = palette().light().color();

        for (int i = 0; i <= settings.numXTicks; ++i)
        {
                int x = rect.left() + (i * (rect.width() - 1) / settings.numXTicks);
                double label = settings.minX + (i * settings.spanX()
                                / settings.numXTicks);
                painter->setPen(quiteDark);
                //   painter->drawLine(x, rect.top(), x, rect.bottom());
                painter->setPen(light);
                painter->drawLine(x, rect.bottom(), x, rect.bottom() + 5);
                painter->drawText(x - 50, rect.bottom() + 5, 100, 20, Qt::AlignHCenter
                                | Qt::AlignTop, QString::number(label));
        }
        for (int j = 0; j <= settings.numYTicks; ++j)
        {
                int y = rect.bottom() - (j * (rect.height() - 1) / settings.numYTicks);
                double label = settings.minY + (j * settings.spanY()
                                / settings.numYTicks);
                painter->setPen(quiteDark);
                //  painter->drawLine(rect.left(), y, rect.right(), y);
                painter->setPen(light);
                painter->drawLine(rect.left() - 5, y, rect.left(), y);
                painter->drawText(rect.left() - Margin, y - 10, Margin - 5, 20,
                                Qt::AlignRight | Qt::AlignVCenter, QString::number(label));
        }
        painter->drawRect(rect.adjusted(0, 0, -1, -1));
}

void PaintChannel::drawCurves(QPainter *painter)
{

    if(!data.isEmpty())
    {
        PaintSettings settings = zoomStack[curZoom];
        int enlarge = 500/settings.spanX();
        QVector<QPointF> tmp;
        tmp.append(data.at(0));
        int dataCount =data.count();
        for(int i=1;i<dataCount-2;i++)
        {
            tmp.append(data.at(i));
            int preX =data.at(i).x();
            double dx = (data.at(i+1).x()-preX)/(float)enlarge;
            int preY =data.at(i).y();
            int dy = (data.at(i+1).y()-preY)/(float)enlarge;
            for(int j=1;j<enlarge;j++)
            {
                tmp.append(QPointF(preX+j*dx,preY+j*dy));
            }

        }
        QRect rect(Margin, 30, width() - 85, height() - 2 * 30);
        painter->setPen(Qt::black);
        if (!rect.isValid())
                return;
        painter->setPen(Qt::red);
        painter->setClipRect(rect.adjusted(+1, +1, -1, -1));
        double Oy=rect.bottom()
                  - ( - settings.minY * (rect.height() - 1) / settings.spanY());
        QPolygonF polyline(tmp.count());
        for (int j = 0; j < tmp.count(); ++j)
        {
                double dx = tmp[j].x() - settings.minX;
                double dy = tmp[j].y() - settings.minY;
                double x = rect.left() + (dx * (rect.width() - 1) / settings.spanX());
                double y = rect.bottom()
                                - (dy * (rect.height() - 1) / settings.spanY());
                polyline[j] = QPointF(x, y);
                painter->drawLine(QPointF(x, y), QPointF(x, Oy));
        }


        painter->drawPolyline(polyline);
    }

}
void PaintChannel::zoomOut()
{
        if (curZoom > 0)
        {
                --curZoom;
                zoomOutButton->setEnabled(curZoom > 0);
                zoomInButton->setEnabled(true);
                zoomInButton->show();
                refreshPixmap();
        }
}

void PaintChannel::zoomIn()
{
        if (curZoom < zoomStack.count() - 1)
        {
                ++curZoom;
                zoomInButton->setEnabled(curZoom < zoomStack.count() - 1);
                zoomOutButton->setEnabled(true);
                zoomOutButton->show();
                refreshPixmap();
        }
}
void PaintChannel::updateRubberBandRegion()
{
        QRect rect = rubberBandRect.normalized();
        update(rect.left(), rect.top(), rect.width(), 1);
        update(rect.left(), rect.top(), 1, rect.height());
        update(rect.left(), rect.bottom(), rect.width(), 1);
        update(rect.right(), rect.top(), 1, rect.height());
}
void PaintChannel::mousePressEvent(QMouseEvent *event)
{
        QRect rect(Margin, Margin, width() - 2 * Margin, height() - 2 * Margin);

        if (event->button() == Qt::LeftButton)
        {
                if (rect.contains(event->pos()))
                {
                        rubberBandIsShown = true;
                        rubberBandRect.setTopLeft(event->pos());
                        rubberBandRect.setBottomRight(event->pos());
                        updateRubberBandRegion();
                        setCursor(Qt::CrossCursor);
                }
        }
}

void PaintChannel::mouseMoveEvent(QMouseEvent *event)
{
        if (rubberBandIsShown)
        {
                updateRubberBandRegion();
                rubberBandRect.setBottomRight(event->pos());
                updateRubberBandRegion();
        }
}

void PaintChannel::mouseReleaseEvent(QMouseEvent *event)
{
        if ((event->button() == Qt::LeftButton) && rubberBandIsShown)
        {
                rubberBandIsShown = false;
                updateRubberBandRegion();
                unsetCursor();

                QRect rect = rubberBandRect.normalized();
                if (rect.width() < 4 || rect.height() < 4)
                        return;
                rect.translate(-Margin, -Margin);

                PaintSettings prevSettings = zoomStack[curZoom];
                PaintSettings settings;
                double dx = prevSettings.spanX() / (width() - 2 * Margin);
                double dy = prevSettings.spanY() / (height() - 2 * Margin);
                settings.minX = prevSettings.minX + dx * rect.left();
                settings.maxX = prevSettings.minX + dx * rect.right();
                settings.minY = prevSettings.maxY - dy * rect.bottom();
                settings.maxY = prevSettings.maxY - dy * rect.top();
                settings.adjust();

                zoomStack.resize(curZoom + 1);
                zoomStack.append(settings);
                zoomIn();
        }
}

void PaintChannel::keyPressEvent(QKeyEvent *event)
{
        switch (event->key())
        {
        case Qt::Key_Plus:
                zoomIn();
                break;
        case Qt::Key_Minus:
                zoomOut();
                break;
        case Qt::Key_Left:
                zoomStack[curZoom].scroll(-1, 0);
                refreshPixmap();
                break;
        case Qt::Key_Right:
                zoomStack[curZoom].scroll(+1, 0);
                refreshPixmap();
                break;
        case Qt::Key_Down:
                zoomStack[curZoom].scroll(0, -1);
                refreshPixmap();
                break;
        case Qt::Key_Up:
                zoomStack[curZoom].scroll(0, +1);
                refreshPixmap();
                break;
        default:
                QWidget::keyPressEvent(event);
        }
}

void PaintChannel::wheelEvent(QWheelEvent *event)
{
        int numDegrees = event->delta() / 8;
        int numTicks = numDegrees / 15;

        if (event->orientation() == Qt::Horizontal)
        {
                zoomStack[curZoom].scroll(numTicks, 0);
        }
        else
        {
                zoomStack[curZoom].scroll(0, numTicks);
        }
        refreshPixmap();
}
